﻿using System;
using System.Collections.Generic;
using System.Linq;
using PSE.Updater.Common.Providers;
using PSE.Updater.Common.Providers.Interfaces;
using PSE.Updater.Common.Gateway.DataContract;

namespace PSE.Updater.Server.Engine.Providers
{
    internal sealed class CacheManager
    {
        #region [ Singleton ]
        private static object _singletonLock = new object();
        private static CacheManager _cacheManager;
        public static CacheManager Instance
        {
            get
            {
                lock (_singletonLock)
                {
                    if (_cacheManager == null)
                        _cacheManager = new CacheManager();

                    return _cacheManager;
                }

            }
        }
        #endregion [ Singleton ]

        private object _cacheManagerLock = new object();
        private Dictionary<string, Dictionary<string, IFileDetails>> _cache = new Dictionary<string, Dictionary<string, IFileDetails>>();

        /// <summary>
        /// Quantidade de Itens no Cache
        /// </summary>
        public int Count
        {
            get
            {
                if (_cache != null)
                    return _cache.Count;

                return 0;
            }
        }

        /// <summary>
        /// Esvazia o Cache
        /// </summary>
        public void Clear()
        {
            lock (_cacheManagerLock)
            {
                _cache.Clear();
            }
        }

        /// <summary>
        /// Adiciona/Atualiza um arquivo no Cache
        /// </summary>
        /// <param name="file">Arquivo a ser adicionado/atualizado</param>
        public void AddOrUpdate(IFileDetails file)
        {
            lock (_cacheManagerLock)
            {
                string productKey = file.SystemName;
                string fileKey = file.FilePath;
                if (_cache.ContainsKey(productKey))
                {
                    if (_cache[productKey].ContainsKey(fileKey))
                    {
                        _cache[productKey].Remove(fileKey);
                    }
                }
                else
                {
                    _cache.Add(productKey, null);
                }

                if (_cache[productKey] == null)
                    _cache[productKey] = new Dictionary<string, IFileDetails>();

                _cache[productKey].Add(fileKey, file);
            }
        }

        /// <summary>
        /// Remove itens por Produto
        /// O cache é dividido por porduto. Cada produto tem vários arquivos.
        /// Ao remover o produtos, automáticamente seus arquivos são removidos
        /// </summary>
        /// <param name="key">Chave do Produto</param>
        public void RemoveProduct(string key)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(key))
                    _cache.Remove(key);
            }
        }

        /// <summary>
        /// Remove um arquivo de produto
        /// </summary>
        /// <param name="key">Chave do Arquivo</param>
        public void RemoveFile(string key)
        {
            lock (_cacheManagerLock)
            {
                bool found = false;
                for (int i = _cache.Count - 1; i >= 0; i--)
                {
                    for (int j = _cache.ElementAt(i).Value.Count - 1; j >= 0; j--)
                    {
                        if (_cache.ElementAt(i).Value.ContainsKey(key))
                        {
                            _cache.ElementAt(i).Value.Remove(key);
                            found = true;
                            break;
                        }
                        if (found)
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Retorna uma lista de todos os arquivos disponíveis
        /// </summary>
        /// <returns>Uma lista de todos os arquivos disponíveis</returns>
        public List<IFileDetails> List()
        {
            lock (_cacheManagerLock)
            {
                List<IFileDetails> files = new List<IFileDetails>();
                foreach (string key in _cache.Keys)
                {
                    foreach (string item in _cache[key].Keys)
                    {
                        if (_cache[key][item].Status == PackageStatus.Valid)
                            files.Add(_cache[key][item]);
                    }
                }
                return files;
            }
        }

        /// <summary>
        /// Retorna uma lista de todos os arquivos disponíveis por Provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <returns>Uma lista de todos os arquivos disponíveis por Provider</returns>
        public List<IFileDetails> List(ProviderType providerType)
        {
            lock (_cacheManagerLock)
            {
                List<IFileDetails> files = new List<IFileDetails>();
                foreach (string key in _cache.Keys)
                {
                    foreach (var item in _cache[key].Where(k => k.Value.ProviderType == providerType))
                    {
                        files.Add(item.Value);
                    }
                }
                return files;
            }
        }

        /// <summary>
        /// Lista os arquivos de um Produto por provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <param name="key">Chave do Produto</param>
        /// <returns>Arquivos de um Produto por provider</returns>
        public List<IFileDetails> List(ProviderType providerType, string productKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (var item in _cache[productKey].Where(k => k.Value.ProviderType == providerType))
                    {
                        files.Add(item.Value);
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma lista de arquivos filtrada por produto, módulo e provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <param name="productKey">Chave do Produto</param>
        /// <param name="moduleKey">Chave do Módulo</param>
        /// <returns>Uma lista de arquivos filtrada por produto, módulo e provider</returns>
        public List<IFileDetails> List(ProviderType providerType, string productKey, string moduleKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (var item in _cache[productKey].Where(k => k.Value.ProviderType == providerType))
                    {
                        if (!string.IsNullOrEmpty(item.Value.ModuleName))
                        {
                            if (item.Value.ModuleName.ToUpper().Contains(moduleKey.ToUpper()))
                                files.Add(item.Value);
                        }
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Lista os arquivos de um Produto
        /// </summary>
        /// <param name="key">Chave do Produto</param>
        /// <returns>Arquivos de um Produto</returns>
        public List<IFileDetails> List(string productKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (string item in _cache[productKey].Keys)
                    {
                        files.Add(_cache[productKey][item]);
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma lista de arquivos filtrada por produto e módulo
        /// </summary>
        /// <param name="productKey">Chave do Produto</param>
        /// <param name="moduleKey">Chave do Módulo</param>
        /// <returns>Uma lista de arquivos filtrada por produto e módulo</returns>
        public List<IFileDetails> List(string productKey, string moduleKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (string key in _cache[productKey].Keys)
                    {
                        var item = _cache[productKey][key];
                        if (!string.IsNullOrEmpty(item.ModuleName))
                        {
                            if (item.ModuleName.ToUpper().Contains(moduleKey.ToUpper()))
                                files.Add(item);
                        }
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma cópia da lista de todos os arquivos disponíveis
        /// </summary>
        /// <returns>Uma cópia da lista de todos os arquivos disponíveis</returns>
        public List<IFileDetails> GetCopyList()
        {
            lock (_cacheManagerLock)
            {
                List<IFileDetails> files = new List<IFileDetails>();
                foreach (string key in _cache.Keys)
                {
                    foreach (string item in _cache[key].Keys)
                        files.Add(_cache[key][item].Clone());
                }
                return files;
            }
        }

        /// <summary>
        /// Retorna uma cópia da lista de arquivos disponíveis de um produto
        /// </summary>
        /// <param name="key">Chave do Produto</param>
        /// <returns>Uma cópia da lista de arquivos disponíveis de um produto</returns>
        public List<IFileDetails> GetCopyList(string productKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (string item in _cache[productKey].Keys)
                        files.Add(_cache[productKey][item].Clone());
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma cópia da lista de arquivos filtrada por produto e módulo
        /// </summary>
        /// <param name="productKey">Chave do Produto</param>
        /// <param name="moduleKey">Chave do Módulo</param>
        /// <returns>Uma cópia da lista de arquivos filtrada por produto e módulo</returns>
        public List<IFileDetails> GetCopyList(string productKey, string moduleKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (string key in _cache[productKey].Keys)
                    {
                        var item = _cache[productKey][key];
                        if (!string.IsNullOrEmpty(item.ModuleName))
                        {
                            if (item.ModuleName.ToUpper().Contains(moduleKey.ToUpper()))
                                files.Add(item.Clone());
                        }
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma lista de todos os arquivos disponíveis por Provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <returns>Uma lista de todos os arquivos disponíveis por Provider</returns>
        public List<IFileDetails> GetCopyList(ProviderType providerType)
        {
            lock (_cacheManagerLock)
            {
                List<IFileDetails> files = new List<IFileDetails>();
                foreach (string key in _cache.Keys)
                {
                    foreach (var item in _cache[key].Where(k => k.Value.ProviderType == providerType))
                    {
                        files.Add(item.Value.Clone());
                    }
                }
                return files;
            }
        }

        /// <summary>
        /// Lista os arquivos de um Produto por provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <param name="key">Chave do Produto</param>
        /// <returns>Arquivos de um Produto por provider</returns>
        public List<IFileDetails> GetCopyList(ProviderType providerType, string productKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (var item in _cache[productKey].Where(k => k.Value.ProviderType == providerType))
                    {
                        files.Add(item.Value.Clone());
                    }
                    return files;
                }
                return null;
            }
        }

        /// <summary>
        /// Retorna uma lista de arquivos filtrada por produto, módulo e provider
        /// </summary>
        /// <param name="providerType"> Tipo do Provider</param>
        /// <param name="productKey">Chave do Produto</param>
        /// <param name="moduleKey">Chave do Módulo</param>
        /// <returns>Uma lista de arquivos filtrada por produto, módulo e provider</returns>
        public List<IFileDetails> GetCopyList(ProviderType providerType, string productKey, string moduleKey)
        {
            lock (_cacheManagerLock)
            {
                if (_cache.ContainsKey(productKey))
                {
                    List<IFileDetails> files = new List<IFileDetails>();
                    foreach (var item in _cache[productKey].Where(k => k.Value.ProviderType == providerType))
                    {
                        if (!string.IsNullOrEmpty(item.Value.ModuleName))
                        {
                            if (item.Value.ModuleName.ToUpper().Contains(moduleKey.ToUpper()))
                                files.Add(item.Value.Clone());
                        }
                    }
                    return files;
                }
                return null;
            }
        }
    }
}